#pragma once

#include "znx_defs.h"
#include "znx_queue.h"
#include "znx_atomic.h"

/**
 * @brief znx_buf设计、及使用注意事项
 * 
 * znx_buf包含了znx_buf_data, znx_buf, znx_buf_chain三个对象
 * 
 * znx_buf_data: 负责管理最底层的数据缓冲区
 * znx_buf: 持有znx_buf_data, 并管理znx_buf_data的可读数据区和可写数据区.
 * znx_buf_chain: 本质上是一个FIFO结构, 从尾部写入, 从头部读取. 是一个单生产者单消费者的结构.
 * 
 * 注意: znx_buf_data和znx_buf均使用引用计数进行管理, 使用完毕后一定要进行free.
 * 将znx_buf_data托管给znx_buf后需要进行free, 因为znx_buf会对该buf_data执行hold操作.
 * 将znx_buf托管给znx_buf_chain之后需要进行free, 因为znx_buf_chain会对znx_buf执行hold操作.
 * 
 * znx_buf_data使用指南:
 * znx_buf_data在创建时就将对应的refer设置为1.
 * - 通过hold操作可以将它的引用计数+1, 而通过free会对它的引用计数-1, 当引用计数为0时将被释放.
 * - 将znx_buf_data托管给znx_buf管理时, znx_buf会对znx_buf_data的引用计数+1.
 * - 使用注意:
 * ```
 *  function() {
 *      // buf_data在创建时, 引用计数为1.
 *      znx_buf_data_t *buf_data = znx_buf_data_alloc();
 *      // 将buf_data托管给buf对象管理, 在znx_buf_alloc中对buf_data执行了hold操作, buf_data引用计数为2.
 *      znx_buf_t *buf = znx_buf_alloc(buf_data, buf_data->data, buf_data->data);
 *      // 注意: 使用完毕后一定要释放, 此时buf_data的引用计数减为1.
 *      znx_buf_data_free(buf_data);
 *      // 注意: 使用完毕后一定要释放, 此时buf的引用计数减少为0
 *      // 会同时释放buf, 并对管理的buf_data进行free.
 *      // 此时buf_data的引用计数减为0, buf_data最终完成释放.
 *      znx_buf_free(buf);
 *  }
 * ```
 * 
 * znx_buf使用指南:
 * 通过znx_buf_alloc创建的znx_buf结构, 会对传入的buf_data执行hold操作.
 * 通过znx_buf_alloc_ext/znx_buf_aligned_alloc_ext创建的结构,
 * - 会创建对应的buf_data, 并将它的引用计数设置为1.
 * - 在alloc_ext中, 先通过buf_alloc创建buf, 将它交给znx_buf进行管理, 再对znx_buf_data进行了free.
 * 当znx_buf托管给znx_buf_chain后, znx_buf在使用完毕后, 需要对znx_buf进行free.
 * ```
 *  function() {
 *      // buf_data->refer == 1
 *      znx_buf_data_t *buf_data = znx_buf_data_alloc();
 * 
 *      // buf_data->refer == 2, buf->refer == 1
 *      znx_buf_t *buf = znx_buf_alloc(buf_data, buf_data->data, buf_data->data);
 * 
 *      // buf_data->refer == 1, buf->refer == 1
 *      znx_buf_data_free(buf_data);
 * 
 *      znx_buf_chain_t *buf_chain = znx_buf_chain_alloc();
 *      // buf_data->refer == 1, buf->refer == 2
 *      znx_buf_chain_enqueue(chain, buf);
 * 
 *      // buf_data->refer == 1, buf->refer == 1
 *      znx_buf_free(buf_data);
 * 
 *      // buf_data->refer == 1, buf->refer == 1
 *      znx_buf_free(buf_data);
 * 
 *      // buf_data->refer == 0, buf->refer == 0, 均进行释放.
 *      znx_buf_chain_free();
 *  }
 * ```
 * 
 * znx_buf_chain使用指南:
 * znx_buf_chain是一个FIFO结构, 使用enqueue可以将buf追加到chain的尾部, 使用dequeue可以返回头部的第一个buf.
 * - enqueue会使buf的refer+1, 而dequeue不会修改buf的refer (这点一定要注意)
 * - 因此通过dequeue获取的buf在使用完毕后应该主动调用free.
 * - 可以想象一下, 如果dequeue自动对refer - 1,
 * - 此时其他部分正好有free操作, 导致该对象的引用计数变为0, 进入了释放流程, 此时再使用该buf, 就出问题了.
 * 
 * znx_buf_chain支持多种灵活的使用方式, 需用户配合使用.
 * deuque: 从头部摘下一个buf, 这个buf不论是否已经释放完毕, 都不会再添加到这个chain的头部.
 * znx_buf_chain_first: 返回头部的第一个客户的buf, 只是简单的返回, 并不会修改chain本身.
 * znx_buf_last: 返回最后一个buf, 这通常是最后一个可写的buf, 只是简单的返回, 并不会修改chain本身.
 * 
 * znx_buf_chain流式数据读写:
 * 
 * znx_buf_chain_first/ znx_buf_chain_next/ znx_buf_chain_consume
 * 通过znx_buf_chain_first返回第一个可读的buf, 配合znx_buf_chain_next可顺序遍历chain中的数据.
 * 最后通过znx_buf_chain_consume可将chain中的数据消费掉.
 * 
 * znx_buf_chain_last: 返回最后一个buf, 这通常是最后一个可写buf.
 * 将数据写入该buf后只需要简单的移动buf的end指针即可.
 * 
 * 
 * chain_copy:
 *
 * znx_buf_chain_copy/copy_n: 对buf_chain中的数据进行拷贝,
 * 新buf_chain除了复用底层的buf_data外，其余的结构都是新创建的.
 * 通过copy接口可实现多个读者消费同一份数据的目的.
 * 
 */

typedef struct {
    znx_atomic_int64_t              refer;
    u_char                          *data;
    size_t                          size;
    unsigned                        aligned:1;
    unsigned                        last:1;
} znx_buf_data_t;

znx_buf_data_t *znx_buf_data_alloc(size_t size);

znx_buf_data_t *znx_buf_data_aligned_alloc(size_t size);

void znx_buf_data_free(znx_buf_data_t *buf_data);

void znx_buf_data_hold(znx_buf_data_t *buf_data);


typedef struct {
    znx_atomic_int64_t              refer;
    znx_buf_data_t                  *data;
    u_char                          *data_end;  // space end in buf data
    u_char                          *start;     // start in buf data
    u_char                          *end;       // end in buf data
    u_char                          *pos;       // consumer pos
    znx_queue_t                     queue;
} znx_buf_t;

znx_buf_t *znx_buf_alloc(znx_buf_data_t *buf_data,
    u_char *start, u_char *end);

znx_buf_t *znx_buf_alloc_ext(size_t size);

znx_buf_t *znx_buf_aligned_alloc_ext(size_t size);

void znx_buf_free(znx_buf_t *buf);

void znx_buf_hold(znx_buf_t *buf);

uint32_t znx_buf_crc32(znx_buf_t *buf);


typedef struct {
    znx_queue_t                     head;
} znx_buf_chain_t;

znx_buf_chain_t *znx_buf_chain_alloc();

void znx_buf_chain_free();

// will call buf_hold for buf
void znx_buf_chain_enqueue(znx_buf_chain_t *chain, znx_buf_t *buf);

// not call buf_free, the user should release it.
znx_buf_t *znx_buf_chain_dequeue(znx_buf_chain_t *chain);

void znx_buf_chain_consume(znx_buf_chain_t *chain, size_t n);

void znx_buf_chain_copy(znx_buf_chain_t *dest, const znx_buf_chain_t *src);

void znx_buf_chain_copy_n(znx_buf_chain_t *dest, const znx_buf_chain_t *src,
    size_t offset, size_t len);

znx_buf_t *znx_buf_chain_last(znx_buf_chain_t *chain);

// return first readable buf
znx_buf_t *znx_buf_chain_first(znx_buf_chain_t *chan);

// return next readable buf at the cur buf.
znx_buf_t *znx_buf_chain_next(znx_buf_chain_t *chain, znx_buf_t *cur);

size_t znx_buf_chain_total_bytes(znx_buf_chain_t *chain);

uint32_t znx_buf_chain_crc32(znx_buf_chain_t *chain);
